/*
 * main.c
 *  Copyright (C) 2011-2012 Dan Reidy <dubkat@ratbox.org>
 *  Copyright (C) 2011-2012 ircd ratbox development team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 *
 *       Foundation, Inc.
 *       59 Temple Place - Suite 330
 *       Boston, MA  02111-1307, USA.
 *
 *
 *  Project Name: ratbox-monitor
 *    Created on: Sep 23, 2010
 *        Author: Daniel J Reidy <dubkat@ratbox.org>
 *            Id: $Id: main.c 50 2012-04-04 03:46:50Z dubkat $
 */
#include <unistd.h>
#include "ratbox_lib.h"
#include "libconfig.h"
#include "common.h"
#include "worker.h"
#include "settings.h"		// our temporary settings
#include "ares.h"
#include "logger.h"
#include "openmp.h"
#include "database.h"

config_t conf;
ares_channel dns;

int read_config(config_t *);
static void dm_fork(void);

void irc_login(struct conn *);
void connect_cb(rb_fde_t *, int, void *);
void connect_to_server(void *);
void read_data(rb_fde_t *, void *);
void irc_process_notice(struct conn *);
void sendto_one(struct conn *serv, const char *format, ...);
void handle(struct conn *, int, char **);
int file_exists(const char *);

static struct conn *
make_conn(void)
{
	return rb_malloc(sizeof(struct conn));
}

int
file_exists(const char *path)
{
	int ac;
	
	logger(DEBUG, "checking path: %s", path);
	
	ac = access(path, F_OK);
	switch(ac) {
		case EACCES : 
			logger(CRIT, "DB: Access Denied");
			exit(EXIT_FAILURE);
			break;
		case ENAMETOOLONG :
			logger(CRIT, "database path name is too long.");
			exit(EXIT_FAILURE);
			break;
		case ENOENT :
			logger(CRIT, "database path does not exist");
			exit(EXIT_FAILURE);
			break;
		case ENOTDIR :
			logger(CRIT, "database directory pathname does not exist.");
			exit(EXIT_FAILURE);
			break;
		case EIO :
			logger(CRIT, "database I/O Error.");
			exit(EXIT_FAILURE);
			break;
			
		case -1 :
			logger(DEBUG, "database does not exist...");
			return 0;
			break;
			
		case 0 :
			logger(DEBUG, "database exists already...");
			return 1;
			break;
			
		default:
			logger(CRIT, "im not sure what got us here. [Code: %d]", ac);
			exit(EXIT_FAILURE);
			break;
	}
}

void
irc_process_notice(struct conn *serv)
{
	int len;
	int parc;
	char readbuf[IRCD_BUFSIZE];
	char *ptr = &readbuf;
	char *parv[MAXPARA];

	while(1)
	{
		len = rb_linebuf_get(&serv->recvq, readbuf, sizeof(readbuf),
				     LINEBUF_COMPLETE, LINEBUF_PARSED);
		if(len == 0)
			break;

		if(*ptr == ':')
			*ptr++;

		parc = rb_string_to_array(ptr, parv, MAXPARA);

		handle_UNKNOWN(serv, parc, parv);
		handle(serv, parc, parv);
	}
}

void
read_data(rb_fde_t *F, void *data)
{
	struct conn *serv = data;
	int length;
	char buffer[IRCD_BUFSIZE];

	while((length = rb_read(serv->F, buffer, sizeof(buffer))) > 0)
	{
		rb_linebuf_parse(&serv->recvq, buffer, length, 0);
	}

	if(length == 0 || (length < 0 && !rb_ignore_errno(errno)))
	{
		logger(CRIT, "Connection aborted.");
	}

	irc_process_notice(serv);
	rb_setselect(F, RB_SELECT_READ, read_data, serv);

}

void
connect_cb(rb_fde_t *F, int status, void *data)
{
	struct conn *serv = data;
	if(status == RB_OK)
	{
		irc_login(serv);
		return;
	}
}

void
connect_to_server(void *data)
{
	int irc_port;
	int family;
	const char *irc_server;
	const char *vhost;
	struct conn *serv;

	struct rb_sockaddr_storage destaddr;
	struct rb_sockaddr_storage bindaddr;

	memset(&destaddr, 0, sizeof(destaddr));
	memset(&bindaddr, 0, sizeof(bindaddr));

	config_lookup_string(&conf, "irc.vhost", &vhost);
	config_lookup_string(&conf, "irc.server", &irc_server);
	config_lookup_int(&conf, "irc.port", &irc_port);

	rb_inet_pton_sock(vhost, (struct sockaddr *)&bindaddr);
	rb_inet_pton_sock(irc_server, (struct sockaddr *)&destaddr);

	family = GET_SS_FAMILY(&destaddr);
	logger(DEBUG, "IPV6: %i IPV4: %i", AF_INET6, AF_INET);
	logger(DEBUG, "FAMILY IS: %i", family);

#ifdef RB_IPV6
	if(family == AF_INET6)
	{
		((struct sockaddr_in6 *)&destaddr)->sin6_port = htons(irc_port);
		((struct sockaddr_in6 *)&bindaddr)->sin6_port = 0;
	}
	else
#endif
	{
		((struct sockaddr_in *)&bindaddr)->sin_port = 0;
		((struct sockaddr_in *)&destaddr)->sin_port = htons(irc_port);
		((struct sockaddr_in *)&destaddr)->sin_family = AF_INET;
	}

	serv = make_conn();

	if((serv->F = rb_socket(family, SOCK_STREAM, 0, "client")) == NULL)
	{
		logger(CRIT, "Error: Opening stream socket to %s: %i", irc_server, errno);
		exit(EXIT_FAILURE);
	}
	rb_connect_tcp(serv->F, (struct sockaddr *)&destaddr,	/*(struct sockaddr *)&bindaddr */
		       NULL, 512, connect_cb, serv, 10);
}

void
irc_login(struct conn *serv)
{
	const char *nickname;
	const char *username;
	const char *gecos;
	config_lookup_string(&conf, "irc.nick", &nickname);
	config_lookup_string(&conf, "irc.user", &username);
	config_lookup_string(&conf, "irc.gecos", &gecos);

	sendto_one(serv, "NICK %s", nickname);
	sendto_one(serv, "USER %s dronemon ratbox-monitor :%s", username, gecos);
	read_data(serv->F, serv);
}

int
main(int argc, char **argv)
{
	struct status *me = &my_status;
	int read_conf;
	int ares;
	const char *dbpath = {0};

	int i;
	int print_version = 0;	/* Value for the "-q" optional argument. */

	for(i = 1; i < argc; i++)
	{

		if(irccmp(argv[i], "-v") == 0 || irccmp(argv[i], "--version") == 0)
			print_version = 1;	/* This is used as a boolean value. */

		if(irccmp(argv[i], "-f") == 0 || irccmp(argv[i], "--foreground") == 0)
			me->foreground = 1;


	}

	if(print_version)
	{
		fprintf(stdout, "Copyright (C) 2011-2012 Dan Reidy <dubkat@gmail.com>\n");
		fprintf(stdout, "%s-%s - %s\n", PACKAGE_NAME, PACKAGE_VERSION, PACKAGE_URL);
		fprintf(stdout, "%s\n", rb_lib_version());
		exit(EXIT_SUCCESS);
	}

	if(!read_config(&conf))
	{
		logger(CRIT, "ERROR: Failed to read config");
		return (EXIT_FAILURE);
	}

	config_lookup_bool(&conf, "read_conf", &read_conf);
	if(!read_conf)
	{
		logger(CRIT, "ERROR: You didn't actually read the config, did you.");
		exit(EXIT_FAILURE);
	}

	ares = ares_library_init(ARES_LIB_INIT_NONE);
	if(ares)
	{
		logger(CRIT, "ERROR: libares returned error code '%i': %s", ares,
		       ares_strerror(ares));
		exit(EXIT_FAILURE);
	}

	if((ares = ares_init(&dns)) != ARES_SUCCESS)
	{
		logger(CRIT, "ERROR: libares channel init failed: %i", ares);
		exit(EXIT_FAILURE);
	}

	logger(DEBUG, "%s/%s", rb_dirname(argv[0]), rb_basename(argv[0]));
	logger(DEBUG, "%s", rb_lib_version());
	logger(DEBUG, "libares resolver: %s", ares_version(NULL));
	
	// setup the database, we cant continue without it //
	config_lookup_string(&conf, "general.database", &dbpath);
	if ( strlen(CheckEmpty(dbpath)) == 0 )
	{
	  logger(CRIT, "You didn't set a path for your database.");
	  exit(EXIT_FAILURE);
	}
	
	// lets play auto-setup //
	if (!file_exists(dbpath)) {
	  database_open(dbpath);
	  database_setup();
	  database_setup_wizard();
	  database_close();
	  exit(EXIT_SUCCESS);
	}
	

	/* ensure our ratbox loop is only executed by the master thread */
#	pragma omp master
	{
#		ifdef HAVE_OPENMP
		logger(INFO, "Threads: (master) I am thread %d out of %d.",
		       omp_get_thread_num(), omp_get_num_threads());
#		else
		logger(INFO, "Threads: (master) I am running single-threaded.");
#		endif

		rb_lib_init(NULL, NULL, NULL, 0, 1024, 1024, 1024);
		rb_linebuf_init(512);
		rb_event_addonce("connect_to_server", connect_to_server, NULL, 1);

		if ( !me->foreground )
			dm_fork();

		rb_lib_loop(-1);
	}

	return 0;
}

void
dm_fork(void)
{
	pid_t p = fork();
	if(p)
	{
		logger(DEBUG, "Forked into background.");
		exit(EXIT_SUCCESS);
	}
	return;
}
